/*
 *  levelUpGame - GameModel.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 *
 * resolution 1400*1050
 * grid size: 11x4, window count: 10x4 (below bottom grid lane is another row of windows: stageLanes)
 */
#include <math.h>
#include <algorithm>
#include <functional>
#include "ofxDirList.h"
#include "LevelUpApp.h"
#include "Ghost.h"
#include "Pickup.h"
#include "Player.h"
#include "GameModel.h"
#include "AStarPathFinder.h"


const int GameModel::BALANCE_MAX = 100; //range is always 0 - BALANCE_MAX, neutral is always BALANCE_MAX / 2
const float GameModel::PLAYER_IMAGE_DIMENSION_RATIO = 4.0f / 3.0f;
const float GameModel::PLAYER_TOP_TO_TOTAL_RATIO =  34.0f / ( 34.0f + 14.0f );
const float GameModel::LANE_THICKNESS = 3.0f;

//--------------------------------------------------------------
GameModel::GameModel( const LevelUpApp& appRef_ )
: p0Colliding( false ), //TEMP
  s( Settings::getInstance() ), appRef( appRef_ ), gameState( GS_IDLE ), paused( false ),
  maxNumPlayers( 3 ), cultureGhost( 0 ), industryGhost( 0 ), playTimeLeft( 0.0f ),
  precountTimeLeft( 0.0f ), timedTextLeft( 0.0f )
{}

GameModel::~GameModel() {
	if ( cultureGhost != 0 ) delete cultureGhost;
	if ( industryGhost != 0 ) delete industryGhost;
}


//--------------------------------------------------------------
void GameModel::setup() {
	initLookupTables();
	
	basePlayerSpeed = ofToInt( *s.getValue( "base_player_speed", "65" ) );
	baseGhostSpeed = ofToInt( *s.getValue( "base_ghost_speed", "50" ) );
	baseBiteSpeed = ofToInt( *s.getValue( "base_bite_speed", "100" ) );
	laneWidth = ofToInt( *s.getValue( "grid_lane_width", "60" ) );
	maxObjectWidth = ofToInt( *s.getValue( "grid_max_object_width", "60" ) );
	stageLaneBottomY = ofToInt( *s.getValue( "grid_stage_bottom_y", "0" ) );
	playerYSubtract = ofToInt( *s.getValue( "grid_player_y_subtract", "0" ) );
	balanceStepPlayer = ofToInt( *s.getValue( "balance_step_player", "1" ) );
	balanceStepGlobal = ofToInt( *s.getValue( "balance_step_global", "1" ) );
	minPickupDistance = (float)ofToInt( *s.getValue( "min_pickup_distance", "25" ) );
	endAnimScaleUp = ofToFloat( *s.getValue( "game_end_anim_scale", "15" ) );
	gameDuration = (float)ofToInt( *s.getValue( "game_duration", "180" ) );
	
	playerDrawSize.x = maxObjectWidth;
	playerDrawSize.y = ( 1.0f / PLAYER_IMAGE_DIMENSION_RATIO ) * playerDrawSize.x;
	
	if ( maxNumPlayers > lutColumns.size() ) {
		ofLog( OF_LOG_ERROR, "(GameModel.cpp:setup()) the game will not behave properly (more players than column lanes on the grid (%i > %i))",
					maxNumPlayers, lutColumns.size() );
	}
	
#ifndef DISABLE_GHOSTS
	initGhosts();
#endif
	
	initPickupImages();
	
	balance.setPosition( lutColumns[0] + 60, lutRows[lutRows.size() - 1] + 50 );
}

//--------------------------------------------------------------
void GameModel::update() {
	float lastGameTime = gameTime;
	gameTime = ofGetElapsedTimef();
	
	if ( precountTimeLeft > 0.0f ) {
		precountTimeLeft -= gameTime - lastGameTime;
		if ( precountTimeLeft <= 0.0f ) precountTimeLeft = 0.0f;
	}
	
	if ( timedTextLeft > 0.0f ) {
		timedTextLeft -= gameTime - lastGameTime;
		if ( timedTextLeft <= 0.0f ) timedTextLeft = 0.0f;
	}
	
	if ( playTimeLeft > 0.0f ) {
		playTimeLeft -= gameTime - lastGameTime;
		if ( playTimeLeft <= 0.0f ) playTimeLeft = 0.0f;
	}
	
	bool ghostsBlocked = getState() != GS_PLAYING;
	if ( cultureGhost != 0 ) cultureGhost->update( ghostsBlocked );
	if ( industryGhost != 0 ) industryGhost->update( ghostsBlocked );
	
	std::for_each ( players.begin(), players.end(), std::mem_fun( &Player::update ) );
	std::for_each ( pickups.begin(), pickups.end(), std::mem_fun( &Pickup::update ) );
}

const ofColor& GameModel::getPlayerColor( int playerIdx_, PLAYER_COLOR_BRIGHTNESS brightness_ ) {
	static const float dimFactor = 0.7f;
	static ofColor color[3], dimmed[3];
	color[0].r = 0x54; color[0].g = 0xff; color[0].b = 0x0; /* green */
	color[1].r = 0xff; color[1].g = 0x0; color[1].b = 0xfd; /* purple */
	color[2].r = 0xff; color[2].g = 0x97; color[2].b = 0x0; /* orange */
	
	dimmed[0].r = color[0].r * dimFactor; dimmed[0].g = color[0].g * dimFactor; dimmed[0].b = color[0].b * dimFactor; /* green */
	dimmed[1].r = color[1].r * dimFactor; dimmed[1].g = color[1].g * dimFactor; dimmed[1].b = color[1].b * dimFactor; /* purple */
	dimmed[2].r = color[2].r * dimFactor; dimmed[2].g = color[2].g * dimFactor; dimmed[2].b = color[2].b * dimFactor; /* orange */
	
	switch ( brightness_ ) {
		case PCB_DIMMED:
			return ( playerIdx_ >= 0 && playerIdx_ < 3 ) ? dimmed[playerIdx_] : dimmed[0];
			break;
		case PCB_NORMAL:
			return ( playerIdx_ >= 0 && playerIdx_ < 3 ) ? color[playerIdx_] : color[0];
	}
}

//--------------------------------------------------------------
float GameModel::getTime() const { return gameTime; }
const GameModel::vec_int& GameModel::getRowLanes() const { return lutRows; }
const GameModel::vec_int& GameModel::getColumnLanes() const { return lutColumns; }
const ofRectangle& GameModel::getBoundingBox() const { return boundingBox; }
const GameModel::vec_PickupP& GameModel::getPickups() const { return pickups; }
GameModel::vec_PickupP& GameModel::getPickups() { return pickups; }
const GameModel::vec_PlayerP& GameModel::getPlayers() const { return players; }
GameModel::vec_PlayerP& GameModel::getPlayers() { return players; }
const Player* GameModel::getPlayer( int index_ ) const { return players.size() > index_ ? players[index_] : 0; };
Player* GameModel::getPlayer( int index_ ) { return players.size() > index_ ? players[index_] : 0; };

//--------------------------------------------------------------
const Ghost* GameModel::getCultureGhost() const { return cultureGhost; }
Ghost* GameModel::getCultureGhost() { return cultureGhost; }
const Ghost* GameModel::getIndustryGhost() const { return industryGhost; }
Ghost* GameModel::getIndustryGhost() { return industryGhost; }
const BalanceMeter& GameModel::getBalanceMeter() const { return balance; }
BalanceMeter& GameModel::getBalanceMeter() { return balance; }

//--------------------------------------------------------------
GAME_STATE GameModel::getState() const { return gameState; }
void GameModel::setState( GAME_STATE state_ ) { gameState = state_; }

//--------------------------------------------------------------
bool GameModel::isPaused() const { return paused; }
void GameModel::setPaused( bool paused_ ) {
	paused = paused_;
	ofLog( OF_LOG_NOTICE, "GAME %sPAUSED", paused ? "" : "UN" );
}

//--------------------------------------------------------------
int GameModel::getBasePlayerSpeed() const { return basePlayerSpeed; }
int GameModel::getBaseGhostSpeed() const { return baseGhostSpeed; }
int GameModel::getBaseBiteSpeed() const { return baseBiteSpeed; }
int GameModel::getLaneWidth() const { return laneWidth; }
int GameModel::getMaxObjectWidth() const { return maxObjectWidth; }
int GameModel::getStageLaneBottomY() const { return stageLaneBottomY; }
int GameModel::getPlayerYSubtract() const { return playerYSubtract; }
int GameModel::getBalanceStepPlayer() const { return balanceStepPlayer; }
int GameModel::getBalanceStepGlobal() const { return balanceStepGlobal; }
int GameModel::getMinPickupDistance() const { return minPickupDistance; }
float GameModel::getEndAnimScaleUp() const { return endAnimScaleUp; }

//--------------------------------------------------------------
void GameModel::resetGhostPositions() {
	if ( cultureGhost ) cultureGhost->setPosition( lutColumns[1], lutRows[0] );
	if ( industryGhost ) industryGhost->setPosition( lutColumns[lutColumns.size() - 2], lutRows[0] );
}

//--------------------------------------------------------------
bool GameModel::isGamePlaying() const { return gameState == GS_PLAYING && playTimeLeft > 0; }
float GameModel::getGameDuration() const { return gameDuration; }
float GameModel::getPlayTimeLeft() const { return playTimeLeft; }
void GameModel::resetPlayTime() {
	playTimeLeft = gameDuration;
	ofLog( OF_LOG_NOTICE, "(GameModel.cpp) reset game time to %.0f seconds", playTimeLeft );
}

//--------------------------------------------------------------
void GameModel::resetBalance() {
	balance.setBalanceFloat( 0 );
	appRef.getGameController().sendBalanceChangedEvent();
}

//--------------------------------------------------------------
float GameModel::getPrecountTimeLeft() const { return precountTimeLeft; }
void GameModel::runPrecount( float seconds_ ) { precountTimeLeft = seconds_; }

//--------------------------------------------------------------
void GameModel::showTimedText( const string& text_, float seconds_ ) {
	timedText = text_;
	timedTextLeft = seconds_;
}

//--------------------------------------------------------------
float GameModel::getTimedTextLeft() const { return timedTextLeft; }
const string& GameModel::getTimedText() const { return timedText; }

//--------------------------------------------------------------
//NOTE: currently min/max = -1/+1
float GameModel::calcPlayerScore( int playerId_ ) const {
	if ( playerId_ < 0 || playerId_ >= players.size() ) return -2;
	
	float gb = getBalanceMeter().getBalanceFloat();
	float pb = players[playerId_]->getTargetBalance().getBalanceFloat();
	
	return gb - pb;
}

//--------------------------------------------------------------
int GameModel::getWinningPlayer() const { return winningPlayer; }
int GameModel::determineWinner() {
	if ( players.size() < maxNumPlayers ) { winningPlayer = -1; return -1; }
	assert( maxNumPlayers == 3 );
	winningPlayer = -1;
	
	//NOTE: or scaled to min/max? (might be more fair)
	
	float gb = getBalanceMeter().getBalanceFloat();
	float tgt1 = players[0]->getTargetBalance().getBalanceFloat();
	float tgt2 = players[1]->getTargetBalance().getBalanceFloat();
	float tgt3 = players[2]->getTargetBalance().getBalanceFloat();
	
	float d1 = fabsf( gb - tgt1 ), d2 = fabsf( gb - tgt2 ), d3 = fabsf( gb - tgt3 );
	
	if ( d1 < d2 && d1 < d3 ) { winningPlayer = 0; }
	else if ( d2 < d1 && d2 < d3 ) { winningPlayer = 1; }
	else { winningPlayer = 2; }
	
	ofLog( OF_LOG_NOTICE, "(GameModel.cpp) Calculating winning player gb=%.2f, tgt={1:%.2f, 2:%.2f, 3:%.2f}",
				gb, tgt1, tgt2, tgt3 );
	ofLog( OF_LOG_NOTICE, "(GameModel.cpp) Delta={1:%.2f, 2:%.2f, 3:%.2f} so player %i won!", d1, d2, d3, winningPlayer );
	
	return winningPlayer;
}

//--------------------------------------------------------------
float GameModel::getPanPosition( float x_ ) const {
	return ofMap( x_, lutColumns[0], lutColumns[lutColumns.size() - 1], -1.0f, 1.0f, true );
}

//--------------------------------------------------------------
const ofPoint& GameModel::getPlayerDrawSize() const { return playerDrawSize; }
int GameModel::getMaxNumPlayers() const { return maxNumPlayers; }

//--------------------------------------------------------------
bool GameModel::setMaxNumPlayers( int maxNumPlayers_ ) {
	if ( maxNumPlayers <= lutColumns.size() ) {
		maxNumPlayers = maxNumPlayers_;
		return true;
	} else {
		ofLog( OF_LOG_ERROR, "(GameModel.cpp) cannot set max. number of players to %i, there are only %i column lanes on the grid",
					maxNumPlayers_, lutColumns.size() );
		return false;
	}
}

//--------------------------------------------------------------
void GameModel::addBalance( int val_, BALANCE_KIND kind_ ) {
	switch( kind_ ) {
		case BK_CULTURE:
			balance.addBalanceCulture( val_ );
			break;
		case BK_INDUSTRY:
			balance.addBalanceIndustry( val_ );
			break;
	}
}

//--------------------------------------------------------------
Player* GameModel::createPlayer( const PlayerInfo& info_ ) {
//ofLog( OF_LOG_NOTICE, "player created (id: %i)", info_.id ); //TEMP
	int startingLane = info_.id + 4;
	Player* p = 0;
	
	if ( info_.id >= 0 ) {
		const ofPoint& drawSize = getPlayerDrawSize();
		p = new Player( appRef );
		
		p->setup( info_ );
		p->setStartingLane( startingLane );
		p->setSize( playerDrawSize.x, playerDrawSize.y );
		//NOTE: set position twice to overwrite last position (in case it gets reverted)
		p->setPosition( ofPoint( lutColumns[startingLane], stageLaneBottomY ) );
		p->setPosition( ofPoint( lutColumns[startingLane], stageLaneBottomY ) );
		
		players.push_back( p );
	}
	
	return p;
}

//--------------------------------------------------------------
bool GameModel::destroyPlayer( Player* p_ ) {
//ofLog( OF_LOG_NOTICE, "player destroyed (id: %i)", p_->getId() ); //TEMP
	vec_PlayerP::iterator it = find( players.begin(), players.end(), p_ );
	if ( it != players.end() ) {
		players.erase( it );
	}
}

//--------------------------------------------------------------
bool GameModel::spawnPickup( BALANCE_KIND balanceType ) {
	AStarPathFinder pf( appRef );
	Pickup* newPickup = new Pickup( appRef );
	bool rv = true;
	
	if ( balanceType == BK_CULTURE ) {
		if ( culturePickupImages.size() > 0 ) {
			int idx = roundf( ofRandom( 0, culturePickupImages.size() - 1 ) );
			newPickup->setImage( culturePickupImages[idx] );
			newPickup->setBalanceType( BK_CULTURE );
		} else {
			ofLog( OF_LOG_WARNING, "(GameModel.cpp) could not spawn culture pickup because none have been loaded" );
			rv = false;
		}
	} else if ( balanceType == BK_INDUSTRY ) {
		if ( industryPickupImages.size() > 0 ) {
			int idx = roundf( ofRandom( 0, industryPickupImages.size() - 1 ) );
			newPickup->setImage( industryPickupImages[idx] );
			newPickup->setBalanceType( BK_INDUSTRY );
		} else {
			ofLog( OF_LOG_WARNING, "(GameModel.cpp) could not spawn industry pickup because none have been loaded" );
			rv = false;
		}
	}
	
	if ( rv ) {
		ofPoint pos;
		bool posIsOk = false;
		int triesLeft = 50;
		
		while ( ! posIsOk && triesLeft > 0 ) {
			generateRandomPositionOnGrid( false, pos );
			newPickup->setPosition( pos );
			posIsOk = true;
			
			for ( int i = 0; i < pickups.size(); ++i ) {
				const ofPoint& pickupPos = pickups[i]->getPosition();
				if ( pf.getDistance( pickupPos, pos ) < minPickupDistance ) {
					posIsOk = false;
					break;
				}
			}
			triesLeft--;
		}
		
		if ( posIsOk ) {
			pickups.push_back( newPickup );
		} else {
			delete newPickup;
			rv = false;
		}
	} else {
		delete newPickup;
	}
	
	return rv;
}

//--------------------------------------------------------------
bool GameModel::dirCoordsAreOnGrid( const ofPoint& c_, DIRECTION dir_, int laneMargin_,
																	 int intersectMargin_, bool includeStageLanes_,
																	 bool allowVerticalWrapping ) const {
	static const int extraWrapRoom = 10;
	bool hitFound = false;
	bool colIntersectHit = false;
	int minColX = lutColumns[0];
	int maxColX = lutColumns[lutColumns.size() - 1];
	int minRowY = allowVerticalWrapping ? 0 - extraWrapRoom : lutRows[0];
	int maxRowY = includeStageLanes_ ? stageLaneBottomY : lutRows[lutRows.size() - 1];
	if ( allowVerticalWrapping ) maxRowY = appRef.getViewController().getCanvasHeight() - 1 + extraWrapRoom;
	
	//do not allow intersection margin to be smaller than lane margin
	if ( intersectMargin_ < laneMargin_ ) intersectMargin_ = laneMargin_;
	
	//first look for hits on column lanes, and take note of any possible intersection hits
	for ( int xi = 0; xi < lutColumns.size(); ++xi ) {
		int xDelta = fabs( c_.x - lutColumns[xi] );
		bool colLaneHit = ( xDelta <= LANE_THICKNESS + laneMargin_ );
		colIntersectHit = ( xDelta <= LANE_THICKNESS + intersectMargin_ );
		
		if ( colLaneHit && c_.y >= minRowY && c_.y <= maxRowY ) {
			if ( dir_ == D_UP || dir_ == D_DOWN ) {
				hitFound = true;
				break;
			}
		}
	}
	
	//if no column lane hit was found, look for row hits and also check if any intersection hits match
	if ( ! hitFound ) {
		for ( int yi = 0; yi < lutRows.size(); ++yi ) {
			int yDelta = fabs( c_.y - lutRows[yi] );
			bool rowLaneHit = ( yDelta <= LANE_THICKNESS + laneMargin_ );
			bool rowIntersectHit = ( yDelta <= LANE_THICKNESS + intersectMargin_ );
			
			if ( rowLaneHit  && c_.x >= minColX && c_.x <= maxColX ) {
				if ( dir_ == D_LEFT || dir_ == D_RIGHT ) {
					hitFound = true;
					break;
				}
			}
			if ( colIntersectHit && rowIntersectHit ) {
				hitFound = true;
				break;
			}
		}
	}
	
	return hitFound;
}

bool GameModel::generateRandomPositionOnGrid( bool includeStageLanes_, ofPoint& pos_ ) const {
	int minColX = lutColumns[0];
	int maxColX = lutColumns[lutColumns.size() - 1];
	int minRowY = lutRows[0];
	int maxRowY = includeStageLanes_ ? stageLaneBottomY : lutRows[lutRows.size() - 1];
	
	bool colLane = ofRandom( 0, 1 );
	
	if ( colLane ) {
		int laneNum = ofRandom( 0, lutColumns.size() - 1 );
		pos_.x = lutColumns[laneNum];
		pos_.y = ofRandom( minRowY, maxRowY );
	} else {
		int laneNum = ofRandom( 0, lutRows.size() - 1 );
		pos_.x = ofRandom( minColX, maxColX );
		pos_.y = lutRows[laneNum];
	}
	
	return true;
}


int GameModel::getNearLane( const GridPoint<int>& p_, NEAR_LANE which_, bool returnIndex_ ) const {
	float result = -1;
	
	if ( which_ == NL_PREV_X || which_ == NL_NEXT_X ) {
		GameModel::vec_int cl = getColumnLanes();
		
		if ( which_ == NL_PREV_X ) {
			for ( int i = cl.size() - 1; i >= 0; --i ) {
				if ( cl[i] <= p_.x ) {
					result = returnIndex_ ? i : cl[i];
					break;
				}
			}
		} else {
			for ( int i = 0; i < cl.size(); ++i ) {
				if ( cl[i] >= p_.x ) {
					result = returnIndex_ ? i : cl[i];
					break;
				}
			}
		}
		
	} else {
		GameModel::vec_int rl = getRowLanes();
		
		if ( which_ == NL_PREV_Y ) {
			for ( int i = rl.size() - 1; i >= 0; --i ) {
				if ( rl[i] <= p_.y ) {
					result = returnIndex_ ? i : rl[i];
					break;
				}
			}
		} else {
			for ( int i = 0; i < rl.size(); ++i ) {
				if ( rl[i] >= p_.y ) {
					result = returnIndex_ ? i : rl[i];
					break;
				}
			}
		}
	}
	
	return result;
}

bool GameModel::fillInLaneIndices( GridPoint<int>& gp_, int laneMargin_, bool fixCoords_ ) const {
	int colHit = -1, rowHit = -1;
	const vec_int& colLanes = getColumnLanes();
	const vec_int& rowLanes = getRowLanes();
	int minColX = colLanes[0];
	int maxColX = colLanes[colLanes.size() - 1];
	int minRowY = rowLanes[0];
	int maxRowY = getStageLaneBottomY();
	
	//first look for hits on column lanes
	for ( int xi = 0; xi < colLanes.size(); ++xi ) {
		int xDelta = fabs( gp_.x - colLanes[xi] );
		bool colLaneHit = ( xDelta <= GameModel::LANE_THICKNESS + laneMargin_ );
		
		if ( colLaneHit && gp_.y >= minRowY && gp_.y <= maxRowY ) { colHit = xi; break; }
	}
	
	//if no column lane hit was found, look for row hits
	for ( int yi = 0; yi < rowLanes.size(); ++yi ) {
		int yDelta = fabs( gp_.y - rowLanes[yi] );
		bool rowLaneHit = ( yDelta <= GameModel::LANE_THICKNESS + laneMargin_ );
		
		if ( rowLaneHit  && gp_.x >= minColX && gp_.x <= maxColX ) { rowHit = yi; break; }
	}
	
	gp_.colIdx = colHit;
	gp_.rowIdx = rowHit;
	
	if ( fixCoords_ ) {
		if ( colHit >= 0 ) gp_.x = colLanes[colHit];
		if ( rowHit >= 0 ) gp_.y = rowLanes[rowHit];
	}
	
	return colHit >= 0 || rowHit >= 0;
}


/*********************
 * PRIVATE FUNCTIONS *
 *********************/

//--------------------------------------------------------------
void GameModel::initLookupTables() {
	int gridCols = ofToInt( *s.getValueInGroup( "grid_columns", "grid", "-1" ) );
	int gridRows = ofToInt( *s.getValueInGroup( "grid_rows", "grid", "-1" ) );
	int gridLaneWidth = ofToInt( *s.getValueInGroup( "grid_lane_width", "grid", "-1" ) );
	boundingBox.x = ofToInt( *s.getValueInGroup( "grid_bb_x", "grid", "-1" ) );
	boundingBox.y = ofToInt( *s.getValueInGroup( "grid_bb_y", "grid", "-1" ) );
	boundingBox.width = ofToInt( *s.getValueInGroup( "grid_bb_w", "grid", "-1" ) );
	boundingBox.height = ofToInt( *s.getValueInGroup( "grid_bb_h", "grid", "-1" ) );
	
	float vertSpace = boundingBox.height / ( gridRows - 1 );
	float horSpace = boundingBox.width / ( gridCols - 1 );
	
	for ( float xp = boundingBox.x; xp <= boundingBox.x + boundingBox.width; xp += horSpace ) {
		lutColumns.push_back( xp );
	}
	
	for ( float yp = boundingBox.y; yp <= boundingBox.y + boundingBox.height; yp += vertSpace ) {
		lutRows.push_back( yp );
	}
	
	ofLog( OF_LOG_NOTICE, "(GameModel.cpp) initialized grid lookup tables (%i columns, %i rows)", lutColumns.size(), lutRows.size() );
}

void GameModel::initGhosts() {
	const string& cultGhostPath = *s.getValue( "ghost_path_culture", "" );
	const string& cultGhostPrefix = *s.getValue( "ghost_prefix_culture", "" );
	const string& indGhostPath = *s.getValue( "ghost_path_industry", "" );
	const string& indGhostPrefix = *s.getValue( "ghost_prefix_industry", "" );
	
	if ( cultGhostPath == "" || cultGhostPrefix == "" ) {
		ofLog( OF_LOG_WARNING, "(GameModel.cpp) no path and/or prefix config keys set for culture ghost" );
	}
	
	if ( indGhostPath == "" || indGhostPrefix == "" ) {
		ofLog( OF_LOG_WARNING, "(GameModel.cpp) no path and/or prefix config keys set for culture ghost" );
	}
	
	cultureGhost = new Ghost( appRef );
	cultureGhost->setup( cultGhostPath, cultGhostPrefix );
	cultureGhost->setBaseSpeed( baseGhostSpeed );
	cultureGhost->setBalanceKind( BK_CULTURE );
	
	industryGhost = new Ghost( appRef );
	industryGhost->setup( indGhostPath, indGhostPrefix );
	industryGhost->setBaseSpeed( baseGhostSpeed );
	industryGhost->setBalanceKind( BK_INDUSTRY );
	
	resetGhostPositions();
}

void GameModel::initPickupImages() {
	const string* culturePath = s.getValue( "pickups_culture", "" );
	const string* industryPath = s.getValue( "pickups_industry", "" );
	
	ofImage* imgP;
	ofxDirList dirList;
	int count;
	
	dirList.allowExt( "png" );
	dirList.allowExt( "PNG" );
	
	count = dirList.listDir( *culturePath );
	for ( int i = 0; i < count; ++i ) {
		imgP = new ofImage(); imgP->loadImage( dirList.getPath( i ) );
		culturePickupImages.push_back( imgP );
	}
	
	count = dirList.listDir( *industryPath );
	for ( int i = 0; i < count; ++i ) {
		imgP = new ofImage(); imgP->loadImage( dirList.getPath( i ) );
		industryPickupImages.push_back( imgP );
	}
}
